/*
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# The Original Code is Copyright (C) 2009 by exherb (htpp://4leaf.me).
# All rights reserved.
#
# The Original Code is: all of this file.
#
# Contributor(s): none yet.
#
*/
#include "HTranslatePlist.h"

HMODULE										CHTranslatePlist::m_coreFundationDllHandle = NULL;
bool										CHTranslatePlist::m_isInited = false;
tf_CFWriteStreamCreateWithFile				CHTranslatePlist::CFWriteStreamCreateWithFile = NULL;
tf_CFReadStreamCreateWithFile				CHTranslatePlist::CFReadStreamCreateWithFile = NULL;
tf_CFStringCreateWithCString				CHTranslatePlist::CFStringCreateWithCString = NULL;
tf_CFURLCreateWithFileSystemPath			CHTranslatePlist::CFURLCreateWithFileSystemPath = NULL;
tf_CFReadStreamOpen							CHTranslatePlist::CFReadStreamOpen = NULL;
tf_CFWriteStreamOpen						CHTranslatePlist::CFWriteStreamOpen = NULL;
tf_CFPropertyListCreateFromStream			CHTranslatePlist::CFPropertyListCreateFromStream = NULL;
tf_CFReadStreamClose						CHTranslatePlist::CFReadStreamClose = NULL;
tf_CFPropertyListIsValid					CHTranslatePlist::CFPropertyListIsValid = NULL;
tf_CFPropertyListWriteToStream				CHTranslatePlist::CFPropertyListWriteToStream = NULL;
tf_CFWriteStreamClose						CHTranslatePlist::CFWriteStreamClose = NULL;
tf_CFRelease								CHTranslatePlist::CFRelease = NULL;
tf_CFURLCreateDataAndPropertiesFromResource	CHTranslatePlist::CFURLCreateDataAndPropertiesFromResource = NULL;
tf_CFPropertyListCreateFromXMLData          CHTranslatePlist::CFPropertyListCreateFromXMLData = NULL;
tf_CFPropertyListCreateXMLData              CHTranslatePlist::CFPropertyListCreateXMLData = NULL;
tf_CFURLWriteDataAndPropertiesToResource    CHTranslatePlist::CFURLWriteDataAndPropertiesToResource = NULL;

CHTranslatePlist::CHTranslatePlist(const std::wstring &coreFundationPath)
{
	if (!m_isInited)
	{
		std::wstring oriPath;
		m_coreFundationDllHandle = LoadLibrary(coreFundationPath.c_str());
		if (!m_coreFundationDllHandle)
		{
			HKEY hSetting = NULL;
			if (::RegCreateKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Apple Inc.\\Apple Mobile Device Support\\Shared", &hSetting) == ERROR_SUCCESS)
			{
				DWORD length = 0;
				if (::RegQueryValueEx(hSetting, L"iTunesMobileDeviceDLL", NULL, NULL, NULL, &length) == ERROR_SUCCESS)
				{
					wchar_t *iTuneDllPathTemp = new wchar_t[length + 1];
					::RegQueryValueEx(hSetting, L"iTunesMobileDeviceDLL", NULL, NULL, (LPBYTE)iTuneDllPathTemp, &length);
					std::wstring pathTail = iTuneDllPathTemp;
					size_t pos = pathTail.rfind(L'\\');
					if (pos != std::wstring::npos)
					{
						pathTail = pathTail.substr(0, pos);
					}
					wchar_t pathTemp[MAX_PATH] = {0};
					oriPath = pathTemp;
					::GetEnvironmentVariable(L"PATH", pathTemp, MAX_PATH);
					pathTail = pathTemp + (L";" + pathTail);
					::SetEnvironmentVariable(L"PATH", pathTail.c_str());

					m_coreFundationDllHandle = LoadLibrary(L"CoreFoundation.dll");
				}
			}
		}
		if (m_coreFundationDllHandle) {
			CFWriteStreamCreateWithFile = (tf_CFWriteStreamCreateWithFile)GetProcAddress(m_coreFundationDllHandle, "CFWriteStreamCreateWithFile");
			CFReadStreamCreateWithFile = (tf_CFReadStreamCreateWithFile)GetProcAddress(m_coreFundationDllHandle, "CFReadStreamCreateWithFile");
			CFStringCreateWithCString = (tf_CFStringCreateWithCString)GetProcAddress(m_coreFundationDllHandle, "CFStringCreateWithCString");
			CFURLCreateWithFileSystemPath = (tf_CFURLCreateWithFileSystemPath)GetProcAddress(m_coreFundationDllHandle, "CFURLCreateWithFileSystemPath");
			CFReadStreamOpen = (tf_CFReadStreamOpen)GetProcAddress(m_coreFundationDllHandle, "CFReadStreamOpen");
			CFWriteStreamOpen = (tf_CFWriteStreamOpen)GetProcAddress(m_coreFundationDllHandle, "CFWriteStreamOpen");
			CFPropertyListCreateFromStream = (tf_CFPropertyListCreateFromStream)GetProcAddress(m_coreFundationDllHandle, "CFPropertyListCreateFromStream");
			CFReadStreamClose = (tf_CFReadStreamClose)GetProcAddress(m_coreFundationDllHandle, "CFReadStreamClose");
			CFPropertyListIsValid = (tf_CFPropertyListIsValid)GetProcAddress(m_coreFundationDllHandle, "CFPropertyListIsValid");
			CFPropertyListWriteToStream = (tf_CFPropertyListWriteToStream)GetProcAddress(m_coreFundationDllHandle, "CFPropertyListWriteToStream");
			CFWriteStreamClose = (tf_CFWriteStreamClose)GetProcAddress(m_coreFundationDllHandle, "CFWriteStreamClose");
			CFRelease = (tf_CFRelease)GetProcAddress(m_coreFundationDllHandle, "CFRelease");
			CFURLCreateDataAndPropertiesFromResource = (tf_CFURLCreateDataAndPropertiesFromResource)GetProcAddress(m_coreFundationDllHandle, "CFURLCreateDataAndPropertiesFromResource");
			CFPropertyListCreateFromXMLData = (tf_CFPropertyListCreateFromXMLData)GetProcAddress(m_coreFundationDllHandle, "CFPropertyListCreateFromXMLData");
			CFPropertyListCreateXMLData = (tf_CFPropertyListCreateXMLData)GetProcAddress(m_coreFundationDllHandle, "CFPropertyListCreateXMLData");
			CFURLWriteDataAndPropertiesToResource = (tf_CFURLWriteDataAndPropertiesToResource)GetProcAddress(m_coreFundationDllHandle, "CFURLWriteDataAndPropertiesToResource");
			if (CFWriteStreamCreateWithFile&&CFReadStreamCreateWithFile&&CFStringCreateWithCString&&CFURLCreateWithFileSystemPath&&CFReadStreamOpen&&
				CFWriteStreamOpen&&CFPropertyListCreateFromStream&&CFReadStreamClose&&CFPropertyListIsValid&&CFPropertyListWriteToStream&&
				CFWriteStreamClose&&CFRelease&&CFURLCreateDataAndPropertiesFromResource&&CFPropertyListCreateFromXMLData&&CFPropertyListCreateXMLData&&
				CFPropertyListCreateXMLData&&CFURLWriteDataAndPropertiesToResource)
			{
				m_isInited = true;
			}
		}
		::SetEnvironmentVariable(L"PATH", oriPath.c_str());
	}
}

CHTranslatePlist::~CHTranslatePlist(void)
{
}

std::string CHTranslatePlist::UTF16ToUTF8(const std::wstring &utf16Str)
{
	int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, utf16Str.c_str(), (int)utf16Str.size(), NULL, 0, NULL, NULL);
	char* strBuf = new char[u8Len + 1];
	::WideCharToMultiByte(CP_UTF8, NULL, utf16Str.c_str(), (int)utf16Str.size(), strBuf, u8Len, NULL, NULL);
	strBuf[u8Len] = '\0';
	std::string str = strBuf;
	delete [] strBuf;
	return str;
}

bool CHTranslatePlist::BinaryToXML( const wchar_t *src, const wchar_t *dst )
{
	if (!m_isInited)
	{
		return false;
	}
	CFStringRef filePathCFString = CFStringCreateWithCString(0, UTF16ToUTF8(src).c_str(), kCFStringEncodingUTF8);
	if (!filePathCFString)
	{
		return false;
	}
	CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filePathCFString, kCFURLWindowsPathStyle, false);
	CFRelease(filePathCFString);
	if (!fileURL)
	{
		return false;
	}

	signed int errorCode;
	CFDataRef resourceData;
	unsigned char status = CFURLCreateDataAndPropertiesFromResource(0, fileURL, &resourceData, NULL, NULL, &errorCode);
	CFRelease(fileURL);

	bool bRtn = false;
	const void * propertyList = NULL;
	if (resourceData)
	{
		CFStringRef errorString;
		propertyList = CFPropertyListCreateFromXMLData(0, resourceData,kCFPropertyListImmutable, &errorString);
		CFRelease(resourceData);
		if (propertyList)
		{
			filePathCFString = CFStringCreateWithCString(0, UTF16ToUTF8(dst).c_str(), kCFStringEncodingUTF8);
			if (filePathCFString)
			{
				fileURL = CFURLCreateWithFileSystemPath(0, filePathCFString, kCFURLWindowsPathStyle, false);
				CFRelease(filePathCFString);
				if (fileURL)
				{
					CFDataRef xmlData = CFPropertyListCreateXMLData(0, propertyList);
					CFRelease(propertyList);
					if (xmlData)
					{
						bRtn = CFURLWriteDataAndPropertiesToResource(fileURL, xmlData, NULL, &errorCode)?true:false;
						CFRelease(fileURL);
						CFRelease(xmlData);
					}
				}
			}
		}
	}
	return bRtn;
}

bool CHTranslatePlist::XMLToBinary( const wchar_t *src, const wchar_t *dst )
{
	if (!m_isInited)
	{
		return false;
	}
	CFStringRef filePathCFString = CFStringCreateWithCString(0, UTF16ToUTF8(src).c_str(), kCFStringEncodingUTF8);
	if (!filePathCFString)
	{
		return false;
	}
	CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filePathCFString, kCFURLWindowsPathStyle, false);
	CFRelease(filePathCFString);
	if (!fileURL)
	{
		return false;
	}

	signed int errorCode;
	CFDataRef resourceData;
	unsigned char status = CFURLCreateDataAndPropertiesFromResource(0, fileURL, &resourceData, NULL, NULL, &errorCode);
	CFRelease(fileURL);

	bool bRtn = false;
	const void * propertyList = NULL;
	if (resourceData)
	{
		CFStringRef errorString;
		propertyList = CFPropertyListCreateFromXMLData(0, resourceData,kCFPropertyListImmutable, &errorString);
		CFRelease(resourceData);
		if (propertyList)
		{
			filePathCFString = CFStringCreateWithCString(0, UTF16ToUTF8(dst).c_str(), kCFStringEncodingUTF8);
			if (filePathCFString)
			{
				fileURL = CFURLCreateWithFileSystemPath(0, filePathCFString, kCFURLWindowsPathStyle, false);
				CFRelease(filePathCFString);
				if (fileURL)
				{
					CFWriteStreamRef writer = CFWriteStreamCreateWithFile(0, fileURL);
					CFRelease(fileURL);
					if (writer)
					{
						CFWriteStreamOpen(writer);
						CFStringRef error;
						bRtn = CFPropertyListWriteToStream(propertyList, writer, 200, &error) == 0?false:true;
						CFWriteStreamClose(writer);
						CFRelease(propertyList);
					}
				}
			}
		}
	}
	return bRtn;
}